在接下來的 API 開發的中途,我想要提出幾個我在這個專案中有用到的方法,有幾個一定要知道的方法,才能使得專案的推進可以有效率之餘又不會留下太多問題。
如同我在前面的文章有提到的部分,除了可以遵循 DRY (Don’t Repeat Yourself) 之餘,還可以重組成複雜的查詢邏輯。藉由商業邏輯的組合,去重複使用已經定義好的條件,可以讓你在往後的維護上,可以更好去修改甚至是重構。
Example:
// Channel.php
use App\Models\Builder\ChannelBuilder;
use Illuminate\Database\Eloquent\Builder;
class Channel extends Base
{
// ...
public static function query(): ChannelBuilder|Builder
{
return parent::query();
}
public function newEloquentBuilder($query): ChannelBuilder
{
return new ChannelBuilder($query);
}
// ...
}
// ChannelBuilder.php
class ChannelBuilder extends Builder
{
public function isPublished(): self
{
return $this->whereStatus(ChannelStatus::published);
}
}
// Usage
$channel = Channel::query()->isPublished()->find();
Eloquent 已經有實作了數個 event ,可以簡單的 hook 特定的 event 去執行一些特別的邏輯,像是:
retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, trashed, forceDeleting, forceDeleted, restoring, restored, and replicating
以下是針對 model 的 primary key
想要改用 uuid
的範例,因為需要先由 application 先產生出 uuid 之後,再寫入到資料庫。另一個原因還有是資料庫尚不支援由 v4
的 uuid 衍生的具有遞增性質的 uuid,所以會需要我們自己產生 uuid。
protected static function boot(): void
{
parent::boot();
self::creating(fn (self $model) => $model->{$model->getKeyName()} = (string) Str::orderedUuid());
self::saving(fn (self $model) => $model->{$model->getKeyName()} = (string) Str::orderedUuid());
}
現在不需要自己管理相關的邏輯,在最新版本的 Laravel 中已經支援 model 的 primary key
改用 uuid
class Article extends Model
{
use HasUuids;
// ...
}
Laravel 中有提供了一個很強大的工具 Servcie container
,運用得當的話可以不僅是享受到依賴反轉(dependency inversion principle)好處,大幅方便測試的撰寫,還可以統一控制 class 的建立用法。
常見的用法如下,由常見的電商多租戶場景為例,我們很容易會需要根據不同的付款方式串接不同第三方支付供應商的 SDK。
將 PaymentGateway
使用 StripePaymentGateway
來綁定實作的 PaymentGateway
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(PaymentGateway::class, StripePaymentGateway::class);
}
}
使用上的話,可以直接使用 type hinet 來讓自動注入的機制知道使用正確的 object。
class OrderController extends Controller
{
public function getOrder(PaymentGateway $paymentGateway, string $id)
{
// ...
$paymentGateway->getTransactionById($id);
// ...
}
}
還可以綁定特定邏輯在 resolving 該 class 時觸發的東西,常見的應該會是 logging
或是 debug
的場景使用。
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->resolving(StripePaymentGateway::class, fn (StripePaymentGateway $payment) => Log::info(printf('[payment history] action:%s', $payment->action));
}
}